home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / music and sound / soundconverter / soundconverter.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  15.9 KB  |  517 lines

  1. //////////
  2. //
  3. //    File:        SoundConverter.c
  4. //
  5. //    Contains:    Sound format conversion sample code.
  6. //
  7. //    Written by:    Bob Aron
  8. //    Revised by:    Tim Monroe
  9. //
  10. //    Copyright:    © 1999 by Apple Computer, Inc., all rights reserved.
  11. //
  12. //    Change History (most recent first):
  13. //
  14. //       <3>         07/07/99    rtm        final tweaks
  15. //       <2>         07/02/99    rtm        fixed problem causing crashes on Windows
  16. //       <1>         07/01/99    rtm        first file from Bob Aron; conversion to personal coding style
  17. //     
  18. //    This is a simple application that demonstrates how to convert some uncompressed audio into any compression
  19. //    format supported by QuickTime 4.0 and Sound Manager 3.4. There are two routines of interest in this file:
  20. //    SndConv_ConvertSomeUncompressedAudio and SndConv_CreateSoundMovie.
  21. //    
  22. //    SndConv_ConvertSomeUncompressedAudio creates a 16-bit, mono, 44.1 kHz sine wave to use as source audio data
  23. //    and then converts it to whatever format is desired. The output format is hard-coded at the beginning of the function
  24. //    SndConv_DriveAudioConversion. SndConv_ConvertSomeUncompressedAudio places the converted data into a handle and then
  25. //    calls SndConv_CreateSoundMovie to add the data to a newly-created movie.
  26. //
  27. //////////
  28.  
  29. #include "SoundConverter.h"
  30.  
  31.  
  32. //////////
  33. //
  34. // SndConv_DriveAudioConversion
  35. // Create some sound data and compress it into a file.
  36. //
  37. //////////
  38.  
  39. void SndConv_DriveAudioConversion (void)
  40. {
  41.     Handle                    mySourceHandle,                                
  42.                             myDestHandle;
  43.     Handle                    myDestCompParamsHandle = NULL;            
  44.     SoundComponentData        mySourceInfo,                                
  45.                             myDestInfo;
  46.     unsigned long            mySourceTotalFrames,
  47.                             myDestTotalFrames;
  48.     short                    myResRefNum = -1;
  49.     CompressionInfo            myDestCompressionInfo;
  50.     Movie                    myMovie = NULL;
  51.     StandardFileReply        myReply;
  52.     long                    myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
  53.     StringPtr                 myPrompt = QTUtils_ConvertCToPascalString(kSaveSoundPrompt);
  54.     StringPtr                 myFileName = QTUtils_ConvertCToPascalString(kSaveSoundFileName);
  55.     OSErr                    myErr = noErr;
  56.  
  57.     //////////
  58.     //
  59.     // allocate handles to hold the uncompressed (source) and compressed (destination) sound data
  60.     //
  61.     //////////
  62.  
  63.     mySourceHandle = NewHandleClear(0);
  64.     myDestHandle = NewHandleClear(0);
  65.     
  66.     if ((mySourceHandle == NULL) || (myDestHandle == NULL))
  67.         goto Bail;
  68.         
  69.     // create some uncompressed, 16-bit, mono, 44.1 kHz sound data; also, fill in the mySourceInfo structure
  70.     // and return the total number of frames, which we'll use later
  71.     myErr = SndConv_UncompressedSineWaveToHandle(mySourceHandle, &mySourceInfo, &mySourceTotalFrames);
  72.     FailIf(myErr != noErr, Bail);
  73.     
  74.     //////////
  75.     //
  76.     // convert the source data
  77.     //
  78.     //////////
  79.  
  80.     // fill in the myDestInfo fields with the data format that you want to convert to
  81.     myDestInfo.flags |= kNoRealtimeProcessing;        // perform in non-real time for best quality (this can also be set to zero)        
  82.     myDestInfo.format = k16BitBigEndianFormat;        // data format
  83.     myDestInfo.numChannels = 1;                        // number of channels (1 for mono, 2 for stereo)
  84.     myDestInfo.sampleSize = 16;                        // sample size (may be left at 16 for all formats except k8BitOffsetBinaryFormat)
  85.     myDestInfo.sampleRate = rate44khz;                // sample rate
  86.     myDestInfo.sampleCount = 0;                        // leave set to 0
  87.     myDestInfo.buffer = NULL;                        // leave set to NULL
  88.     myDestInfo.reserved = 0;                        // leave set to 0
  89.     
  90.     // convert the source data
  91.     myErr = SndConv_ConvertSomeUncompressedAudio(    mySourceHandle,
  92.                                                     mySourceInfo,
  93.                                                     mySourceTotalFrames,
  94.                                                     myDestHandle,
  95.                                                     myDestInfo, 
  96.                                                      &myDestTotalFrames,
  97.                                                      &myDestCompressionInfo,
  98.                                                      &myDestCompParamsHandle);
  99.     FailIf(myErr != noErr, Bail);
  100.  
  101.     //////////
  102.     //
  103.     // create a new movie to hold the converted sound data
  104.     //
  105.     //////////
  106.  
  107.     // elicit a location from the user
  108.     StandardPutFile(myPrompt, myFileName, &myReply);
  109.     if (!myReply.sfGood)
  110.         goto Bail;
  111.     
  112.     if (myReply.sfReplacing) {
  113.         myErr = FSpDelete(&myReply.sfFile);
  114.         if (myErr != noErr)
  115.             goto Bail;
  116.     }
  117.  
  118.     myErr = CreateMovieFile(&myReply.sfFile, FOUR_CHAR_CODE('TVOD'), smSystemScript, myFlags, &myResRefNum, &myMovie);
  119.     FailIf(myErr != noErr, Bail);
  120.  
  121.     // put the newly-converted data into the movie
  122.     myErr = SndConv_CreateSoundMovie(    myDestHandle,
  123.                                         myResRefNum,
  124.                                         myMovie,
  125.                                         myDestInfo,
  126.                                         &myDestCompParamsHandle, 
  127.                                         myDestCompressionInfo,
  128.                                         myDestTotalFrames);
  129.     FailIf(myErr != noErr, Bail);    
  130.  
  131. Bail:
  132.  
  133.     free(myPrompt);
  134.     free(myFileName);
  135.     
  136.     if (myMovie != 0)
  137.         DisposeMovie(myMovie);
  138.         
  139.     if (myResRefNum != -1)
  140.         CloseMovieFile(myResRefNum);
  141.  
  142.     if (myDestHandle != NULL)
  143.         DisposeHandle(myDestHandle);
  144.         
  145.     if (mySourceHandle != NULL)
  146.         DisposeHandle(mySourceHandle);
  147.         
  148.     if (myDestCompParamsHandle != NULL)
  149.         DisposeHandle(myDestCompParamsHandle);
  150.     
  151.     return;
  152. }
  153.  
  154.  
  155. //////////
  156. //
  157. // SndConv_ConvertSomeUncompressedAudio
  158. // Convert the specified sound data.
  159. //
  160. // This routine expects uncompressed, 16-bit mono data as source data.
  161. //
  162. //////////
  163.  
  164.  
  165. OSErr SndConv_ConvertSomeUncompressedAudio (    Handle theSourceHandle,
  166.                                                 SoundComponentData theSourceInfo,
  167.                                                 unsigned long theSourceTotalFrames, 
  168.                                                 Handle theDestHandle,
  169.                                                 SoundComponentData theDestInfo,
  170.                                                 unsigned long *theDestFramesMoved, 
  171.                                                 CompressionInfo *theDestCompInfo,
  172.                                                 Handle *theDestCompParams)
  173. {
  174.     SoundConverter        myConverter;
  175.     Handle                myDestCompParamsHandle = NULL;
  176.     unsigned long        myNumFramesLeft,
  177.                         mySourceFrames, 
  178.                         myDestFrames,
  179.                         mySourceBytes, 
  180.                         myDestBytes,
  181.                         myDataOffset = 0;
  182.     Ptr                    myDestPtr = NULL;
  183.     UInt16                hasOptionsDialog = 0;
  184.     short                 mySourceBytesPerFrame = 2;        // 16-bit mono uncompressed data is 2 bytes per frame
  185.     OSErr                myErr = noErr;
  186.  
  187.     //////////
  188.     //
  189.     // open a sound converter
  190.     //
  191.     //////////
  192.     
  193.     myErr = SoundConverterOpen(&theSourceInfo, &theDestInfo, &myConverter);                        
  194.     if (myErr != noErr)
  195.         goto Bail;
  196.  
  197.     // see if the destination format has an options dialog and open if it does
  198.     myErr = SoundConverterGetInfo(myConverter, siOptionsDialog, &hasOptionsDialog);
  199.     if ((myErr == noErr) && hasOptionsDialog) {    
  200.         myErr = SoundConverterSetInfo(myConverter, siOptionsDialog, NULL);
  201.         FailIf(myErr != noErr, Bail);
  202.     }
  203.     
  204.     // ignore this error, since some codecs don't use this selector (makes QDesign work)
  205.     myErr = SoundConverterSetInfo(myConverter, siCompressionChannels, &theDestInfo.numChannels);        
  206.  
  207.     //////////
  208.     //
  209.     // get the sound converter-specific settings.
  210.     //
  211.     //////////
  212.  
  213.     // Not all sound converters have custom settings; in this case myDestCompParamsHandle will be unchanged.
  214.     // we need to return these settings to the caller, as they are required by the decompressor to be able
  215.     // to decompress the compressed audio. If this audio is stored in a QuickTime movie, these parameters
  216.     // will be stored in a SoundDescriptionExtension of type siDecompressionParams.
  217.     myErr = SoundConverterGetInfo(myConverter, siCompressionParams, &myDestCompParamsHandle);
  218.     
  219.     // if any compression params were passed back, send it to the sound converter now
  220.     if (myErr == noErr) {
  221.         HLockHi(myDestCompParamsHandle);
  222.         myErr = SoundConverterSetInfo(myConverter, siCompressionParams, *myDestCompParamsHandle);            
  223.         HUnlock(myDestCompParamsHandle);
  224.         FailIf(myErr != noErr, Bail);
  225.     } else {
  226.         // no audio atom list to deal with, so set to NULL
  227.         myDestCompParamsHandle = NULL;
  228.     }
  229.  
  230.     // get sound converter buffer size info
  231.     myErr = SoundConverterGetBufferSizes(myConverter, kMaxBufferSize, &mySourceFrames, &mySourceBytes, &myDestBytes);    
  232.     FailIf(myErr != noErr, Bail);
  233.     
  234.     // create destination data buffer
  235.     myDestPtr = NewPtrClear(myDestBytes);                                                
  236.     myErr = MemError();
  237.     FailIf(myErr != noErr, Bail);
  238.  
  239.     //////////
  240.     //
  241.     // convert the sound to the desired output format
  242.     //
  243.     //////////
  244.  
  245.     myErr = SoundConverterBeginConversion(myConverter);                                        
  246.     FailIf(myErr != noErr, Bail);
  247.  
  248.     // get info about destination compression
  249.     // We need to return this information to the caller as they are required
  250.     // in order to put this compressed audio into a QuickTime movie. This information
  251.     // will go into the Version 1 Sound Description Handle.
  252.  
  253.     myErr = SoundConverterGetInfo(myConverter, siCompressionFactor, theDestCompInfo);                    
  254.     if (myErr != noErr) {
  255.         myErr = GetCompressionInfo(fixedCompression, theDestInfo.format, theDestInfo.numChannels, theDestInfo.sampleSize, theDestCompInfo);    
  256.         FailIf(myErr != noErr, Bail);
  257.     }
  258.  
  259.     // myBytesPerFrame is not filled in by GetInfo, so we set it here
  260.     theDestCompInfo->bytesPerFrame = theDestCompInfo->bytesPerPacket * theDestInfo.numChannels;    
  261.     
  262.     // initialize destination total frame count to zero
  263.     *theDestFramesMoved = 0;                                                                                                                        
  264.     
  265.     myNumFramesLeft = theSourceTotalFrames;
  266.     HLockHi(theSourceHandle);
  267.  
  268.     // loop through buffers of size mySourceFrames
  269.     while (myNumFramesLeft > 0) {
  270.         // if there are fewer frames remaining than our source buffer size,
  271.         // we're near the end of our data, so get what's remaining
  272.         if (myNumFramesLeft < mySourceFrames)                                                                                        
  273.             mySourceFrames = myNumFramesLeft;                                            
  274.  
  275.         myErr = SoundConverterConvertBuffer(myConverter, *theSourceHandle + myDataOffset, mySourceFrames, myDestPtr, &myDestFrames, &myDestBytes);
  276.         FailIf(myErr != noErr, Bail);
  277.  
  278.         // place the converted data into a handle
  279.         myErr = PtrAndHand(myDestPtr, theDestHandle, myDestBytes);
  280.         FailIf(myErr != noErr, Bail);
  281.  
  282.         // move offset to appropriate place in source data
  283.         myDataOffset += mySourceFrames * mySourceBytesPerFrame;
  284.         
  285.         // keep track of total frames returned by converter
  286.         *theDestFramesMoved += myDestFrames;
  287.  
  288.         myNumFramesLeft -= mySourceFrames;
  289.     }
  290.  
  291.     // end the conversion, and see if we get back a few more bytes of data    
  292.     myErr = SoundConverterEndConversion(myConverter, myDestPtr, &myDestFrames, &myDestBytes);        
  293.     FailIf(myErr != noErr, Bail);
  294.  
  295.     HUnlock(theSourceHandle);
  296.  
  297.     // place any leftover converted data into the handle
  298.     myErr = PtrAndHand(myDestPtr, theDestHandle, myDestBytes);
  299.     FailIf(myErr != noErr, Bail);
  300.  
  301.     *theDestFramesMoved += myDestFrames;    
  302.  
  303. Bail:
  304.  
  305.     *theDestCompParams = myDestCompParamsHandle;
  306.  
  307.     if (myDestPtr != NULL)
  308.         DisposePtr(myDestPtr);
  309.         
  310.     if (myConverter != NULL)
  311.         SoundConverterClose(myConverter);
  312.         
  313.     return(myErr);
  314. }
  315.  
  316.  
  317. //////////
  318. //
  319. // SndConv_CreateSoundMovie
  320. // Create a sound movie from the specified handle of audio data.
  321. //
  322. //////////
  323.  
  324. OSErr SndConv_CreateSoundMovie (    Handle theDestAudioData,
  325.                                     short theMovieRefNum, 
  326.                                     Movie theMovie,
  327.                                     SoundComponentData theDestInfo, 
  328.                                     Handle *theDestCompParams,
  329.                                     CompressionInfo theDestCompInfo, 
  330.                                     unsigned long theDestFrameCount)
  331. {
  332.     Track                        myTrack = NULL;
  333.     Media                        myMedia = NULL;
  334.     TimeScale                    myTimeScale;
  335.     SoundDescriptionV1Handle    mySampleDesc = NULL;
  336.     unsigned long                myLengthInSamples;
  337.     OSErr                        myErr = noErr;
  338.         
  339.     //////////
  340.     //                
  341.     // create the movie track and media
  342.     //                
  343.     //////////
  344.  
  345.     myTrack = NewMovieTrack(theMovie, 0, 0, kFullVolume);                    
  346.     myErr = GetMoviesError();
  347.     FailIf(myErr != noErr, Bail);
  348.  
  349.     myTimeScale = (theDestInfo.sampleRate >> 16);
  350.     
  351.     // set new track to be a sound track
  352.     myMedia = NewTrackMedia(myTrack, SoundMediaType, myTimeScale, NULL, 0);     
  353.     myErr = GetMoviesError();    
  354.     FailIf(myErr != noErr, Bail);
  355.  
  356.     // start a media editing session
  357.     myErr = BeginMediaEdits(myMedia);                                        
  358.     FailIf(myErr != noErr, Bail);
  359.     
  360.     //////////
  361.     //                
  362.     // create a sound sample description
  363.     //                
  364.     //////////
  365.  
  366.     // use the SoundDescription format 1 because it adds fields for data size information
  367.     // and is required by AddSoundDescriptionExtension if an extension is required for the compression format
  368.     mySampleDesc = (SoundDescriptionV1Handle)NewHandleClear(sizeof(SoundDescriptionV1));    
  369.     myErr = MemError();
  370.     FailIf(myErr != noErr, Bail);
  371.     
  372.     // fill in the fields of the sample description
  373.     (*mySampleDesc)->desc.descSize            = sizeof(SoundDescriptionV1);
  374.     (*mySampleDesc)->desc.dataFormat        = theDestInfo.format;                        
  375.     (*mySampleDesc)->desc.resvd1            = 0;                                    
  376.     (*mySampleDesc)->desc.resvd2            = 0;                                    
  377.     (*mySampleDesc)->desc.dataRefIndex        = 1;                                    
  378.     (*mySampleDesc)->desc.version            = 1;                                    
  379.     (*mySampleDesc)->desc.revlevel            = 0;                                    
  380.     (*mySampleDesc)->desc.vendor            = 0;                                    
  381.     (*mySampleDesc)->desc.numChannels        = theDestInfo.numChannels;                    
  382.     (*mySampleDesc)->desc.sampleSize        = theDestInfo.sampleSize;                     
  383.     (*mySampleDesc)->desc.compressionID        = 0;                                    
  384.     (*mySampleDesc)->desc.packetSize        = 0;                                    
  385.     (*mySampleDesc)->desc.sampleRate        = theDestInfo.sampleRate;
  386.     (*mySampleDesc)->samplesPerPacket         = theDestCompInfo.samplesPerPacket;
  387.     (*mySampleDesc)->bytesPerPacket         = theDestCompInfo.bytesPerPacket;
  388.     (*mySampleDesc)->bytesPerFrame             = theDestCompInfo.bytesPerFrame;
  389.     (*mySampleDesc)->bytesPerSample         = theDestCompInfo.bytesPerSample;
  390.  
  391.     // not all compression formats have compression params, so we only need to add a
  392.     // sound description extension for those that do 
  393.     if (*theDestCompParams != NULL)
  394.         AddSoundDescriptionExtension((SoundDescriptionHandle)mySampleDesc, *theDestCompParams, siDecompressionParams);
  395.  
  396.     //////////
  397.     //                
  398.     // add samples to the media
  399.     //                
  400.     //////////
  401.  
  402.     myLengthInSamples = theDestFrameCount * theDestCompInfo.samplesPerPacket;
  403.     myErr = AddMediaSample(    myMedia,
  404.                             theDestAudioData,
  405.                             0,
  406.                             theDestFrameCount * theDestCompInfo.bytesPerFrame,
  407.                             1, 
  408.                              (SampleDescriptionHandle)mySampleDesc,
  409.                              myLengthInSamples,
  410.                              0,
  411.                              NULL);
  412.     FailIf(myErr != noErr, Bail);
  413.         
  414.  
  415.     myErr = EndMediaEdits(myMedia);
  416.     FailIf(myErr != noErr, Bail);
  417.  
  418.     //////////
  419.     //                
  420.     // add the media to the track
  421.     //                
  422.     //////////
  423.  
  424.     myErr = InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), FixRatio(1, 1));
  425.     FailIf(myErr != noErr, Bail);
  426.  
  427.     myErr = AddMovieResource(theMovie, theMovieRefNum, NULL, NULL);
  428.     FailIf(myErr != noErr, Bail);
  429.         
  430. Bail:
  431.     if (mySampleDesc != NULL)    
  432.         DisposeHandle((Handle)mySampleDesc);
  433.         
  434.     return(myErr);
  435. }
  436.  
  437.  
  438. //////////
  439. //
  440. // SndConv_UncompressedSineWaveToHandle
  441. // Create some 16-bit, monophonic, 44.1kHz data and stick it into a handle.
  442. //
  443. //////////
  444.  
  445. OSErr SndConv_UncompressedSineWaveToHandle (Handle theData, SoundComponentData *theCompInfo, unsigned long *theTotalFrames)
  446. {
  447.     long            myIndex, myByteCounter;
  448.     double_t        mySinResult, myRate;
  449.     long            myPlotMe;
  450.     float            myPi = 3.14159;
  451.     Byte            *myBufferPtr = NULL;
  452.     Byte            *myStartOfBufferPtr = NULL;
  453.     unsigned long    myBytesCount = 0;
  454.     long            myFramesInBuffer = 10240;
  455.     long             myHz = kConcertA,
  456.                     mySeconds = 3;
  457.     short            myBytesPerFrame;
  458.     OSErr            myErr = noErr;
  459.  
  460.     *theTotalFrames = 0;
  461.     
  462.     theCompInfo->flags = 0;
  463.     theCompInfo->format = k16BitBigEndianFormat;
  464.     theCompInfo->numChannels = 1;
  465.     theCompInfo->sampleSize = 16;
  466.     theCompInfo->sampleRate = rate44khz;
  467.     theCompInfo->sampleCount = mySeconds * (theCompInfo->sampleRate >> 16);
  468.     theCompInfo->buffer = NULL;
  469.     theCompInfo->reserved = 0;
  470.     
  471.     myBytesPerFrame = theCompInfo->sampleSize / 8;
  472.  
  473.     myBufferPtr = (Byte *)NewPtrClear(myFramesInBuffer * myBytesPerFrame);    
  474.     FailWithAction(myBufferPtr == NULL, myErr = memFullErr, Bail);
  475.     
  476.     myStartOfBufferPtr = myBufferPtr;
  477.         
  478.     myRate = theCompInfo->sampleRate / 65536.0;
  479.  
  480.     //////////
  481.     //          2 π hz            
  482.     //    (sin(i * ------)) * samplesize
  483.     //             rate                                                                                            
  484.     //////////
  485.  
  486.     for (myIndex = 0; myIndex < theCompInfo->sampleCount; myIndex++) {
  487.         mySinResult = sin(myIndex * ((2 * myPi * myHz) / myRate));
  488.         myPlotMe = mySinResult * ((1 << (theCompInfo->sampleSize - 1)) - 1);                                
  489.                     
  490.         for (myByteCounter = 1; myByteCounter <= (theCompInfo->sampleSize / 8); myByteCounter++, myBytesCount++, myBufferPtr++) {
  491.             *myBufferPtr = myPlotMe >> (theCompInfo->sampleSize - (myByteCounter * 8));
  492.         }
  493.         
  494.         if (myBytesCount == myFramesInBuffer * myBytesPerFrame) {
  495.             myErr = PtrAndHand(myStartOfBufferPtr, theData, myBytesCount);
  496.             FailIf(myErr != noErr, Bail);
  497.                 
  498.             *theTotalFrames += myBytesCount / myBytesPerFrame;                        
  499.             myBytesCount = 0;                                                    // reset bytes count
  500.             myBufferPtr = myStartOfBufferPtr;                                    // reset myBufferPtr
  501.         }
  502.     }
  503.  
  504.     myErr = PtrAndHand(myStartOfBufferPtr, theData, myBytesCount);
  505.     FailIf(myErr != noErr, Bail);
  506.  
  507.     *theTotalFrames += myBytesCount / myBytesPerFrame;
  508.     
  509. Bail:
  510.     if (myBufferPtr != NULL)
  511.         DisposePtr((Ptr)myBufferPtr);
  512.         
  513.     return(myErr);
  514. }
  515.  
  516.  
  517.